package com.zxy.supplier_system.server.service

import com.zxy.supplier_system.server.controller.DirectSaleClientsOrderController
import com.zxy.supplier_system.server.controller.DirectSaleOrderController
import com.zxy.supplier_system.server.controller.DirectSaleOrdersItemController
import com.zxy.supplier_system.server.entity.*
import com.zxy.supplier_system.server.repository.*
import com.zxy.supplier_system.server.utils.IntIdAndName
import com.zxy.supplier_system.server.utils.orElse404
import com.zxy.supplier_system.server.utils.toIntIdAndName
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Slice
import org.springframework.data.jpa.domain.Specification
import org.springframework.data.repository.findByIdOrNull
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.server.ResponseStatusException
import java.time.OffsetDateTime

@Service
class DirectSaleOrderService(
    private val directSaleOrderRepository: DirectSaleOrderRepository,
    private val directSaleOrderItemRepository: DirectSaleOrderItemRepository,
    private val employeeRepository: EmployeeRepository,
    private val systemServiceService: SystemServiceService,
    private val goodsRepository: GoodsRepository,
    private val employeeService: EmployeeService,
    private val employeeOperateRecordService: EmployeeOperateRecordService,
    private val directSaleClientRepository: DirectSaleClientRepository,
) {

    @Transactional(readOnly = true)
    fun queryDirectSaleOrder(
        queryDirectSaleOrderRequest: DirectSaleOrderController.QueryDirectSaleOrderRequest,
        pageable: Pageable
    ): Page<DirectSaleOrderController.QueryDirectSaleOrderResponse> {

        return this.directSaleOrderRepository.findAll(
            Specification.allOf(
                DirectSaleOrderRepository.Specifications.clientIn(queryDirectSaleOrderRequest.clientIds),
                DirectSaleOrderRepository.Specifications.endDateLessThanOrEqualTo(queryDirectSaleOrderRequest.endDate),
                DirectSaleOrderRepository.Specifications.startDateGreaterThanOrEqualTo(queryDirectSaleOrderRequest.startDate),
                DirectSaleOrderRepository.Specifications.createdByIn(queryDirectSaleOrderRequest.createdByIds),
            ), pageable
        ).map { directSaleOrder ->
            DirectSaleOrderController.QueryDirectSaleOrderResponse(
                directSaleOrder.id,
                directSaleOrder.createdBy.let {
                    IntIdAndName(it.id!!, it.name)
                },
                directSaleOrder.createdDateTime,
                directSaleOrder.items.size,
                directSaleOrder.client.let {
                    IntIdAndName(it.id, it.name)
                },
                directSaleOrder.submittedDateTime,
                directSaleOrder.paidPrice,
                directSaleOrder.shouldPaidPrice,
            )
        }
    }

    @Transactional
    fun createDirectSaleOrder(marketId: Int): Int {
        val directSaleOrder = this.directSaleOrderRepository.save(DirectSaleOrder().apply {
            this.client = directSaleClientRepository.findByIdOrNull(marketId).orElse404()
        })
        this.employeeOperateRecordService.createDirectSaleOrder(directSaleOrder)
        return directSaleOrder.id
    }

    @Transactional
    fun updateOrderGoods(
        id: Int,
        updateOrderGoodsRequest: DirectSaleOrderController.UpdateOrderGoodsRequest
    ) {
        val directSaleOrder = this.directSaleOrderRepository.findByIdOrNull(id).orElse404()
        directSaleOrder.items.clear()
        directSaleOrder.items.addAll(
            updateOrderGoodsRequest.goodsList.mapIndexed { index, it ->
                DirectSaleOrderItem().apply {
                    this.order = directSaleOrder
                    this.amount = it.amount
                    this.goods =
                        goodsRepository.findByIdAndSystemServiceAndDisableIsFalse(it.id, systemServiceService.getCurrentSystemService())
                            .orElse404()
                    this.sequence = index
                }
            }
        )
        this.directSaleOrderRepository.save(directSaleOrder)
    }

    @Transactional(readOnly = true)
    fun getDirectSaleOrderDetail(id: Int, includeItems: Boolean): DirectSaleOrderController.DirectSaleOrderDetail {
        val directSaleOrder = this.directSaleOrderRepository.findByIdOrNull(id).orElse404()
        return DirectSaleOrderController.DirectSaleOrderDetail(
            directSaleOrder.id,
            directSaleOrder.createdDateTime,
            directSaleOrder.createdBy.toIntIdAndName(),
            directSaleOrder.client.toIntIdAndName(),
            if (!includeItems) emptyList() else directSaleOrder.items.map { item ->
                val goods = item.goods
                goods.let {
                    DirectSaleOrderController.DirectSaleOrderItemResponse(
                        it.id,
                        it.name, it.barcode,
                        it.price,
                        it.amount,
                        it.createdDateTime,
                        it.lastModifiedDateTime,
                        it.brand?.toIntIdAndName(),
                        it.category?.toIntIdAndName(),
                        it.factory?.toIntIdAndName(),
                        item.amount,
                        it.boxSpecification,
                    )
                }
            },
            directSaleOrder.submittedDateTime,
            directSaleOrder.paidRecords.size,
            directSaleOrder.paidPrice,
            directSaleOrder.shouldPaidPrice,
        )
    }

    @Transactional
    fun addItem(orderId: Int, addDirectSaleOrdersItemRequest: DirectSaleOrdersItemController.AddDirectSaleOrdersItemRequest) {
        val directSaleOrder = this.directSaleOrderRepository.findByIdOrNull(orderId).orElse404()
        directSaleOrder.items.add(DirectSaleOrderItem().apply {
            this.order = directSaleOrder
            this.goods = goodsRepository.findByIdAndSystemServiceAndDisableIsFalse(
                addDirectSaleOrdersItemRequest.goodsId,
                systemServiceService.getCurrentSystemService()
            ).orElse404()
            this.amount = addDirectSaleOrdersItemRequest.amount
            this.sequence = directSaleOrder.items.nextSequence
            this.price = addDirectSaleOrdersItemRequest.price
        })
        this.directSaleOrderRepository.save(directSaleOrder)
        this.employeeOperateRecordService.addDirectSaleOrderItem(directSaleOrder)
    }

    @Transactional
    fun deleteItem(orderId: Int, goodsId: Int) {
        val directSaleOrderItem = this.directSaleOrderItemRepository.findByIdOrNull(DirectSaleOrderItem.UnionId().apply {
            this.order = orderId
            this.goods = goodsId
        })
        if (directSaleOrderItem != null) {
            this.employeeOperateRecordService.deleteDirectSaleOrderItem(directSaleOrderItem)
            this.directSaleOrderItemRepository.delete(directSaleOrderItem)
        }
    }

    @Transactional
    fun updateItemAmount(
        goodsId: Int,
        orderId: Int,
        updateItemAmountRequest: DirectSaleOrdersItemController.UpdateItemAmountRequest
    ) {
        val directSaleOrderItem = this.directSaleOrderItemRepository.findByIdOrNull(DirectSaleOrderItem.UnionId().apply {
            this.order = orderId
            this.goods = goodsId
        }).orElse404()
        directSaleOrderItem.amount = updateItemAmountRequest.amount
        this.directSaleOrderItemRepository.save(directSaleOrderItem)
        this.employeeOperateRecordService.updateDirectSaleOrderItemAmount(directSaleOrderItem)
    }

    fun getAllEmployees(): List<IntIdAndName> {
        return this.employeeRepository.findAll(
            EmployeeRepository.Specifications.haveDirectSaleOrder().and(
                SystemServiceRepository.Specifications.systemServiceEquals(
                    { root -> root },
                    systemServiceService.getCurrentSystemService()
                )
            )
        ).map {
            IntIdAndName(it.id!!, it.name)
        }
    }

    @Transactional
    fun queryDirectSaleOrderInClient(
        marketId: Int,
        pageable: Pageable
    ): Slice<DirectSaleClientsOrderController.QueryDirectSaleClientOrderInDirectSaleClientResponse> {
        return this.directSaleOrderRepository.findAllByClient_Id(marketId, pageable).map {
            DirectSaleClientsOrderController.QueryDirectSaleClientOrderInDirectSaleClientResponse(
                it.id,
                it.createdBy.toIntIdAndName(),
                it.createdDateTime,
                it.items.size,
                it.submittedDateTime,
            )
        }
    }

    fun queryOrderItemsInOrder(
        orderId: Int,
        pageable: Pageable
    ): Page<DirectSaleOrdersItemController.OrderItemInOrderSummary> {
        return this.directSaleOrderItemRepository.findAllByOrder_IdOrderBySequenceAsc(orderId, pageable).map {
            val goods = it.goods
            DirectSaleOrdersItemController.OrderItemInOrderSummary.fromGoodsAndAmount(goods, it.amount)
        }
    }

    fun exists(orderId: Int, goodsId: Int): Boolean {
        return this.directSaleOrderItemRepository.existsByOrder_IdAndGoods_Id(orderId, goodsId)
    }

    fun exists(orderId: Int, barcode: String): Boolean {
        return this.directSaleOrderItemRepository.existsByOrder_IdAndGoods_Barcode(orderId, barcode)
    }


    @Transactional
    fun addItem(orderId: Int, barcode: String): DirectSaleOrdersItemController.OrderItemInOrderSummary {
        val currentSystemService = systemServiceService.getCurrentSystemService()
        val directSaleOrder = this.directSaleOrderRepository.findByIdAndClient_SystemService(
            orderId,
            currentSystemService
        ).orElse404()
        val goods =
            goodsRepository.findByBarcodeAndSystemServiceAndDisableIsFalse(barcode, currentSystemService)
                .orElse404()
        return orderItemInOrderSummary(directSaleOrder, goods)
    }

    @Transactional
    fun submitDirectSaleOrder(id: Int) {
        val directSaleOrder = this.directSaleOrderRepository.findByIdAndClient_SystemService(
            id,
            systemServiceService.getCurrentSystemService()
        ).orElse404()
        if (directSaleOrder.submittedDateTime == null) {
            directSaleOrder.submittedDateTime = OffsetDateTime.now()
            this.directSaleOrderRepository.save(directSaleOrder)
            this.employeeOperateRecordService.submitDirectSaleOrder(directSaleOrder)
            directSaleOrder.items.forEach {
                it.goods.amount -= it.amount
            }

            this.goodsRepository.saveAll(directSaleOrder.items.map { it.goods })
        } else {
            throw ResponseStatusException(HttpStatus.BAD_REQUEST)
        }
    }

    @Transactional
    fun addItem(orderId: Int, goodsId: Int): DirectSaleOrdersItemController.OrderItemInOrderSummary {
        val currentSystemService = systemServiceService.getCurrentSystemService()
        val directSaleOrder = this.directSaleOrderRepository.findByIdAndClient_SystemService(
            orderId,
            currentSystemService
        ).orElse404()
        val goods =
            goodsRepository.findByIdAndSystemServiceAndDisableIsFalse(goodsId, currentSystemService)
                .orElse404()
        return orderItemInOrderSummary(directSaleOrder, goods)
    }

    private fun orderItemInOrderSummary(
        directSaleOrder: DirectSaleOrder,
        goods: Goods
    ): DirectSaleOrdersItemController.OrderItemInOrderSummary {
        val amount = 1
        directSaleOrder.items.add(DirectSaleOrderItem().apply {
            this.order = directSaleOrder
            this.goods =
                goods
            this.amount = amount
            this.sequence = directSaleOrder.items.nextSequence
        })
        this.directSaleOrderRepository.save(directSaleOrder)
        this.employeeOperateRecordService.addDirectSaleOrderItem(directSaleOrder)
        return DirectSaleOrdersItemController.OrderItemInOrderSummary.fromGoodsAndAmount(goods, amount)
    }

    @Transactional
    fun toStore(id: Int): MarketDeliveryOrder {
        TODO()
        // TODO
//        val directSaleOrder = this.directSaleOrderRepository.findByIdAndClient_SystemService(
//            id,
//            systemServiceService.getCurrentSystemService()
//        ).orElse404()
//        if (directSaleOrder.submitDateTime == null) {
//            throw ResponseStatusException(HttpStatus.BAD_REQUEST, "订单未提交")
//        }
//        var marketDeliveryOrder = marketDeliveryOrderRepository.save(MarketDeliveryOrder().apply {
//            this.market = directSaleOrder.market
//            this.order = directSaleOrder
//            this.id = id
//        })
//        for (item in directSaleOrder.goodsList) {
//            val goods = item.goods
//            goods.amount -= item.amount
//            goodsRepository.save(goods)
//        }
//        marketDeliveryOrder = marketDeliveryOrder.apply {
//            this.goodsList = directSaleOrder.goodsList.map { directSaleOrderItem ->
//                MarketDeliveryOrderItem().apply {
//                    this.order = marketDeliveryOrder
//                    this.amount = directSaleOrderItem.amount
//                    this.goods = directSaleOrderItem.goods
//                }
//            }.toMutableList()
//        }
//        marketDeliveryOrder = this.marketDeliveryOrderRepository.save(marketDeliveryOrder)
//        this.employeeOperateRecordService.deliveryDirectSaleOrder(directSaleOrder)
//        return marketDeliveryOrder
    }

    @Transactional
    fun pay(id: Int, request: DirectSaleOrderController.PayDirectSaleOrderRequest) {
        val directSaleOrder = this.directSaleOrderRepository.findByIdAndClient_SystemService(
            id,
            systemServiceService.getCurrentSystemService()
        ).orElse404()
        directSaleOrder.paidRecords.add(DirectSaleOrderPayRecord().apply {
            this.price = request.price
            this.order = directSaleOrder
        })
        this.directSaleOrderRepository.save(directSaleOrder)
    }
}
